using System;
using System.Collections.Generic;
using System.Xml;
using System.Windows.Forms;
using System.Drawing;
using System.IO;

namespace Woz
{
    public sealed class InterfaceManager
    {
        private InterfaceManager()
        {
            // create the dictionary of our woz buttons
            this.mWozButtons = new Dictionary<string, Woz.Button>();
            
            // create the dictionary of comboboxes
            this.mComboBoxes = new Dictionary<string, ToolStripComboBox>();
            
            // create the dictionary of drop down buttons
            this.mDropDownButtons = new Dictionary<string, ToolStripDropDownButton>();

            // create the menu
            this.mMenuStrips = new Dictionary<string, MenuStrip>();

            // create the toolstrips
            this.mToolStrips = new Dictionary<string, ToolStrip>();
        }

        public static InterfaceManager Instance
        {
            get
            {
                if (sInstance == null)
                {
                    sInstance = new InterfaceManager();
                }
                
                return sInstance;
            }
        }

        public Dictionary<string, Woz.Button> Buttons
        {
            get
            {
                return this.mWozButtons;
            }
        }

        public Dictionary<string, ToolStripComboBox> ComboBoxes
        {
            get
            {
                return this.mComboBoxes;
            }
        }

        public Dictionary<string, ToolStripDropDownButton> DropDownButtons
        {
            get
            {
                return this.mDropDownButtons;
            }
        }

        public Dictionary<string, MenuStrip> MenuStrips
        {
            get
            {
                return this.mMenuStrips;
            }
        }

        public Dictionary<string, ToolStrip> ToolStrips
        {
            get
            {
                return this.mToolStrips;
            }
        }

        public bool Initialize()
        {
            // first load up the widgets
            if (!this.InitializeWidgets())
            {
                return false;
            }

            // now build the menus
            if (!this.InitializeMenus())
            {
                return false;
            }

            // and the toolstrips
            if (!this.InitializeToolStrips())
            {
                return false;
            }

            // send back a successful messege
            return true;
        }

        public void InvokeDropDownButtonItemClick(ToolStripDropDownButton pDropDownButton, ToolStripMenuItem pMenuItem)
        {
            // merely invoke our private function
            this.cDropDownItemClicked(pDropDownButton, new ToolStripItemClickedEventArgs(pMenuItem));
        }

        private bool InitializeWidgets()
        {
            // first get all the xml files in the button folder
            string[] widgetFileNames = System.IO.Directory.GetFiles(Path.Combine(Woz.StaticVariables.Instance.MediaFolder, "widgets/"));

            // we now have a list of files so create button definitions for each one
            foreach (string widgetFileName in widgetFileNames)
            {
                // send this file name to our function that will read it
                if (!this.ReadWidgetFileXml(widgetFileName))
                {
                    // if it fails then return false
                    return false;
                }
            }

            // send back a successful messege
            return true;
        }

        private bool InitializeMenus()
        {
            // first get all the xml files in the button folder
            string[] menuFileNames = System.IO.Directory.GetFiles(Path.Combine(Woz.StaticVariables.Instance.MediaFolder, "menus/"));

            // we now have a list of files so create button definitions for each one
            foreach (string menuFileName in menuFileNames)
            {
                // send this file name to our function that will read it
                if (!this.ReadMenuFileXml(menuFileName))
                {
                    // if it fails then return false
                    return false;
                }
            }

            // send back a successful messege
            return true;
        }

        private bool InitializeToolStrips()
        {
            // first get all the xml files in the toolstrips folder
            string[] toolStripFileNames = System.IO.Directory.GetFiles(Path.Combine(Woz.StaticVariables.Instance.MediaFolder, "toolstrips/"));

            // we now have a list of files so create button definitions for each one
            foreach (string toolStripFileName in toolStripFileNames)
            {
                // send this file name to our function that will read it
                if (!this.ReadToolStripFileXml(toolStripFileName))
                {
                    // if it fails then return false
                    return false;
                }
            }

            // send back a successful messege
            return true;
        }

        #region READING GLOBAL AND INDIVIDUAL WIDGET FILE XML

        private void ReadWozButtonXml(XmlReader pXmlReader)
        {
            // woz button that well create and eventually add to the dictionary
            Woz.Button wozButton;

            // simple file buffers
            string bgString, shortcutString, buttonName;

            // reader will become 'button' start element
            pXmlReader.Read();

            // read the icon file name and the shortcut string
            bgString = pXmlReader.GetAttribute("bg");
            shortcutString = pXmlReader.GetAttribute("shortcut");

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'value' start element
            pXmlReader.Read();

            // reader will become 'value' end element
            buttonName = pXmlReader.ReadString();

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'button' end element
            pXmlReader.Read();

            // we have all info now create and initialize the button
            wozButton = new Woz.Button(buttonName);
            wozButton.Background = bgString;
            wozButton.Shortcut = shortcutString;

            // add this woz button to our dictionary
            this.mWozButtons[buttonName] = wozButton;
        }

        private void ReadComboBoxXml(XmlReader pXmlReader)
        {
            // combobox that well eventually add to the dictionary
            ToolStripComboBox comboBox;

            // list of combox items
            List<string> comboBoxItems = new List<string>();

            // some file reading buffers
            string comboBoxName, defaultItem;

            // reader will become 'combobox' start element
            pXmlReader.Read();

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'value' start element
            pXmlReader.Read();

            // reader will become 'value' end element
            comboBoxName = pXmlReader.ReadString();

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'items' start element
            pXmlReader.Read();

            // get the default item attribute
            defaultItem = pXmlReader.GetAttribute("default");

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'item' start element
            pXmlReader.Read();

            // loop through all the items
            while (pXmlReader.Name == "item")
            {
                // add this item the our list
                // reader will become 'item' end element
                comboBoxItems.Add(pXmlReader.ReadString());

                // reader will become whitespace
                pXmlReader.Read();

                // reader will become either 'item' start element or 'items' end element
                pXmlReader.Read();
            }

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'combobox' end element
            pXmlReader.Read();

            // we have all info now create and initialize the combobox
            comboBox = new ToolStripComboBox(comboBoxName);
            comboBox.Items.AddRange(comboBoxItems.ToArray());
            comboBox.SelectedItem = defaultItem;
            
            // add this combobox to our dictionary
            this.mComboBoxes[comboBoxName] = comboBox;
        }

        private void ReadDropDownButtonXml(XmlReader pXmlReader)
        {
            // combobox that well eventually add to the dictionary
            ToolStripDropDownButton dropDownButton;
            ToolStripMenuItem menuItem;

            // some file reading buffers
            string dictionaryKey, text, imageName, defaultItem, itemImageName, itemText;
            bool showImage, showText, exclusiveChoice;

            // reader will become 'dropdownbutton' start element
            pXmlReader.Read();

            // get the attributes that we need
            text = pXmlReader.GetAttribute("text");
            imageName = pXmlReader.GetAttribute("bg");
            showText = bool.Parse(pXmlReader.GetAttribute("showText"));
            showImage = bool.Parse(pXmlReader.GetAttribute("showImage"));
            exclusiveChoice = bool.Parse(pXmlReader.GetAttribute("exclusiveChoice"));

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'value' start element
            pXmlReader.Read();

            // get all the default item attribute
            defaultItem = pXmlReader.GetAttribute("default");

            // read the dictionary key
            dictionaryKey = pXmlReader.ReadString();

            // reader will become 'value' end element
            dropDownButton = new ToolStripDropDownButton(text);

            // add the image if the resource manager has it loaded
            if (Woz.ResourceManager.Instance.Images.ContainsKey(imageName))
            {
                dropDownButton.Image = Woz.ResourceManager.Instance.Images[imageName];
            }

            // set the display style on this
            if (showText && showImage)
            {
                dropDownButton.DisplayStyle = ToolStripItemDisplayStyle.ImageAndText;
            }

            else if (showText)
            {
                dropDownButton.DisplayStyle = ToolStripItemDisplayStyle.Text;
            }

            else if (showImage)
            {
                dropDownButton.DisplayStyle = ToolStripItemDisplayStyle.Image;
            }

            else
            {
                dropDownButton.DisplayStyle = ToolStripItemDisplayStyle.None;
            }

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'items' start element
            pXmlReader.Read();

            // get the default item attribute
            defaultItem = pXmlReader.GetAttribute("default");

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'item' start element
            pXmlReader.Read();

            // loop through all the items
            while (pXmlReader.Name == "item")
            {
                // add this item the our list
                
                // get the item image name and text
                itemImageName = pXmlReader.GetAttribute("bg");
                itemText = pXmlReader.ReadString();

                // make sure this image exists
                if(Woz.ResourceManager.Instance.Images.ContainsKey(itemImageName))
                {
                    // it exists so add this item with the image
                    menuItem = new ToolStripMenuItem(itemText, Woz.ResourceManager.Instance.Images[itemImageName]);
                }
                else
                {
                    // image doesnt exist so just create the item with the text
                    menuItem = new ToolStripMenuItem(itemText);
                }

                // set the name on this drop down item so we can retrieve it later
                menuItem.Name = itemText;

                // add this item to the drop down button
                dropDownButton.DropDownItems.Add(menuItem);

                // reader will become whitespace
                pXmlReader.Read();

                // reader will become either 'item' start element or 'items' end element
                pXmlReader.Read();
            }

            // reader will become whitespace
            pXmlReader.Read();

            // reader will become 'dropdownbutton' end element
            pXmlReader.Read();

            //dropDown.SelectedItem = defaultItem;

            // check if they wanted the item choosing on this to be exclusive
            if (exclusiveChoice)
            {
                dropDownButton.DropDownItemClicked += new ToolStripItemClickedEventHandler(this.cDropDownItemClicked);
            }

            // add this drop down box to our dictionary
            this.mDropDownButtons[dictionaryKey] = dropDownButton;
        }
        
        private bool ReadWidgetFileXml(string pWidgetFileName)
        {
            // the file xml container well use to read the button file
            XmlTextReader widgetFile;

            // try to open this file
            try
            {
                widgetFile = new XmlTextReader(pWidgetFileName);
            }

            catch (XmlException)
            {
                // just return false from here
                return false;
            }

            // if we made it this far then this file has been successfully opened

            // proceed to try and read it
            widgetFile.MoveToContent();
            widgetFile.Read();
            widgetFile.Read();

            // loop while we didnt get to the end of the widgets list
            while (widgetFile.Name != "widgets")
            {
                // check if were about to read a button
                if (widgetFile.Name == "button")
                {
                    // we are so send this xml reader to the function that reads the button xml
                    this.ReadWozButtonXml(widgetFile.ReadSubtree());
                }

                // or check if were about to read a combobox
                else if (widgetFile.Name == "combobox")
                {
                    // we are so send this xml reader to the function that reads the combobox xml
                    this.ReadComboBoxXml(widgetFile.ReadSubtree());
                }

                // or check if were about to read a drop down button
                else if (widgetFile.Name == "dropdownbutton")
                {
                    // we are so send this xml reader to the function that reads the combobox xml
                    this.ReadDropDownButtonXml(widgetFile.ReadSubtree());
                }

                // read the end of that widget
                widgetFile.ReadEndElement();
                
                // reader will become next element
                widgetFile.Read();
            }

            // close the file
            widgetFile.Close();

            // send back a successful messege
            return true;
        }
        #endregion

        #region READING MENU FILE XML

        private void ReadGroupItems(XmlTextReader pMenuFile, ToolStripMenuItem pParent, string pGroupType)
        {
            while((pMenuFile.Name != pGroupType || pMenuFile.IsStartElement()) && pMenuFile.Name != "menu")
            {
                if (pMenuFile.Name == "item")
                {
                    // read the name of the item
                    string itemName = pMenuFile.ReadString();

                    // a flag whether or not this is a seperator
                    bool isSeperator = true;

                    // first check if its a seperator
                    foreach (char letter in itemName)
                    {
                        // they must all be dashes to be a seperator
                        if (letter != '-')
                        {
                            // its not a dash so set the flag
                            isSeperator = false;

                            // and break out of here
                            break;
                        }
                    }

                    // handle the seperator case
                    if (isSeperator)
                    {
                        pParent.DropDownItems.Add(new ToolStripSeparator());
                    }

                    // its not a seperator
                    else
                    {
                        // a flag whether or not to add this item
                        bool addItem = true;

                        // loop through and see if there is already an item like this
                        for (int i = 0; i < pParent.DropDownItems.Count; i++)
                        {
                            // but only do so if its not a seperator
                            if (pParent.DropDownItems[i] is ToolStripMenuItem)
                            {
                                // check if this item has the same name
                                if (pParent.DropDownItems[i].Text == itemName)
                                {
                                    // it does so set our flag
                                    addItem = false;

                                    // and leave the loop
                                    break;
                                }
                            }
                        }

                        // make sure there is a woz button with this name
                        if (this.mWozButtons.ContainsKey(itemName))
                        {
                            // and also make sure this item isnt already here under this parent
                            if (addItem)
                            {
                                // finally add this item to the parent
                                pParent.DropDownItems.Add(this.mWozButtons[itemName].MenuItem);
                            }
                        }
                    }

                    // new line
                    pMenuFile.Read();           
                    
                    // next tag
                    pMenuFile.Read();
                }

                else if (pMenuFile.Name == "item_group")
                {
                    // get the name of this item group
                    string itemGroupName = pMenuFile.GetAttribute("name");

                    // the new item that were trying to name
                    ToolStripMenuItem itemGroupMenuItem = null;

                    // loop through and see if there is already an item like this
                    for (int i = 0; i < pParent.DropDownItems.Count; i++)
                    {
                        // but only do so if its not a seperator
                        if (pParent.DropDownItems[i] is ToolStripMenuItem)
                        {
                            // check if this item has the same name
                            if (pParent.DropDownItems[i].Text == itemGroupName)
                            {
                                // it does so set this item as the group item
                                itemGroupMenuItem = (ToolStripMenuItem)pParent.DropDownItems[i];

                                // and leave the loop
                                break;
                            }
                        }
                    }

                    // check if our group item has still not been populated
                    if (itemGroupMenuItem == null)
                    {
                        // create a new toolstrip item
                        itemGroupMenuItem = new ToolStripMenuItem(itemGroupName);

                        // add this item to the parent
                        pParent.DropDownItems.Add(itemGroupMenuItem);
                    }

                    // new line
                    pMenuFile.Read(); 
                    
                    // next tag
                    pMenuFile.Read(); 

                    // call this function with this new item as the parent
                    this.ReadGroupItems(pMenuFile, itemGroupMenuItem, "item_group");
                }

                else if (pMenuFile.Name == "main_group")
                {
                    // get the name of this main group
                    string mainGroupName = pMenuFile.GetAttribute("name");

                    // the new item that were trying to create
                    ToolStripMenuItem mainGroupMenuItem = null;

                    // loop through and see if there is already an item like this
                    foreach(ToolStripMenuItem menuItem in this.mCurrentMenu.Items)
                    {
                        // check if this item has the same name
                        if(menuItem.Text == mainGroupName)
                        {
                            // it does so set our variable
                            mainGroupMenuItem = menuItem;

                            // and leave the loop
                            break;
                        }
                    }

                    // only create and add this main group if its not already there
                    if (mainGroupMenuItem == null)
                    {
                        // create a new toolstrip item
                        mainGroupMenuItem = new ToolStripMenuItem(mainGroupName);

                        // add this group to the menu
                        this.mCurrentMenu.Items.Add(mainGroupMenuItem);
                    }

                    // new line
                    pMenuFile.Read();

                    // next tag
                    pMenuFile.Read(); 

                    // set this new item as the parent and recall this function
                    this.ReadGroupItems(pMenuFile, mainGroupMenuItem, "main_group");
                }
            }

            // new line
            pMenuFile.Read(); 
            
            // next tag
            pMenuFile.Read(); 
        }

        private bool ReadMenuFileXml(string pMenuFileName)
        {
            // the file xml container well use to read the button file
            XmlTextReader menuFile;

            // try to open this file
            try
            {
                menuFile = new XmlTextReader(pMenuFileName);
            }

            catch (XmlException)
            {
                // just return false from here
                return false;
            }

            // if we made it this far then this file has been successfully opened

            // proceed to try and read it
            
            // read menu tag
            menuFile.MoveToContent();

            // get the name of the menu
            string menuName = menuFile.GetAttribute("name");

            // create a new menu with this name
            this.mMenuStrips[menuName] = new MenuStrip();

            // set this menu as the current menu
            this.mCurrentMenu = this.mMenuStrips[menuName];

            // read the new line
            menuFile.Read();

            // read the next tag
            menuFile.Read();

            // call our superly recursive function to take care of things from here
            this.ReadGroupItems(menuFile, null, "main_group");
                
            // close the file
            menuFile.Close();

            // send back a successful messege
            return true;
        }
        #endregion

        #region READING TOOLSTRIP FILE XML

        private bool ReadToolStripFileXml(string pToolStripFileName)
        {
            // the file xml container well use to read the button file
            XmlTextReader toolStripFile;

            // simple file buffer
            string itemName;

            // try to open this file
            try
            {
                toolStripFile = new XmlTextReader(pToolStripFileName);
            }

            catch (XmlException)
            {
                // just return false from here
                return false;
            }

            // if we made it this far then this file has been successfully opened

            // read the toolstrip tag
            toolStripFile.MoveToContent();

            // get the name of the toolstrip
            string toolStripName = toolStripFile.GetAttribute("name");

            // create a new entry in our toolstrip list for this
            this.mToolStrips[toolStripName] = new ToolStrip();

            // set the name of the toolstrip
            this.mToolStrips[toolStripName].Name = toolStripName;

            // set this as our current toolstrip
            this.mCurrentToolStrip = this.mToolStrips[toolStripName];

            // read the new line
            toolStripFile.Read();

            // read the next tag
            toolStripFile.Read();

            // loop while we didnt get to the end of the buttons list
            while (toolStripFile.Name != "toolstrip")
            {
                // check if this is indeed an item
                if (toolStripFile.Name != "item")
                {
                    // go to the next button
                    toolStripFile.ReadToNextSibling("item");

                    // its not an item so move onto the next one
                    continue;
                }

                // read its value
                itemName = toolStripFile.ReadString();

                // read new line
                toolStripFile.Read();

                // read next tag
                toolStripFile.Read();

                // add this item to the current toolstrip

                // a flag whether or not this is a seperator
                bool isSeperator = true;

                // first check if its a seperator
                foreach (char letter in itemName)
                {
                    // they must all be dashes to be a seperator
                    if (letter != '-')
                    {
                        // its not a dash so set the flag
                        isSeperator = false;

                        // and break out of here
                        break;
                    }
                }

                // handle the seperator case
                if (isSeperator)
                {
                    // add a seperator onto the current strip
                    this.mCurrentToolStrip.Items.Add(new ToolStripSeparator());
                }

                // its not a seperator
                else
                {
                    // first check the buttons we wanna first make sure it exists
                    if (this.mWozButtons.ContainsKey(itemName))
                    {
                        // it exists so go ahead and add it
                        this.mCurrentToolStrip.Items.Add(this.mWozButtons[itemName].ToolStripItem);
                    }

                    // its not in the buttons so see if it exists in the comboboxes
                    else if (this.mComboBoxes.ContainsKey(itemName))
                    {
                        // it exists so go ahead and add it
                        this.mCurrentToolStrip.Items.Add(this.mComboBoxes[itemName]);
                    }

                    // its not in the buttons or comboboxes so see if it exists in the drop down buttons
                    else if (this.mDropDownButtons.ContainsKey(itemName))
                    {
                        // it exists so go ahead and add it
                        this.mCurrentToolStrip.Items.Add(this.mDropDownButtons[itemName]);
                    }
                }
            }

            // close the file
            toolStripFile.Close();

            // send back a successful messege
            return true;
        }

        #endregion

        private void cDropDownItemClicked(object pSender, ToolStripItemClickedEventArgs pEventArgs)
        {
            // first un check all of the buttons
            ToolStripDropDownButton dropDownButton = (ToolStripDropDownButton)pSender;
            ToolStripMenuItem clickedMenuItem = ((ToolStripMenuItem)pEventArgs.ClickedItem);

            // make sure this button isnt already clicked
            if (clickedMenuItem.Checked)
            {
                return;
            }

            // this is a new button so go ahead and check it

            // first uncheck all of items in the drop down
            foreach (ToolStripItem toolStripItem in dropDownButton.DropDownItems)
            {
                ((ToolStripMenuItem)toolStripItem).Checked = false;
            }

            // then check this new one
            clickedMenuItem.Checked = true;

            // and set the image on the drop down button to the same one as was clicked
            dropDownButton.Image = clickedMenuItem.Image;

            // set the text on the drop down box
            int colonIndex = dropDownButton.Text.IndexOf(':');
            if (colonIndex > 0)
            {
                dropDownButton.Text = dropDownButton.Text.Remove(colonIndex);
            }
            dropDownButton.Text += ": " + clickedMenuItem.Text;

            // finally invoke our own event for this
            if (this.DropDownItemClicked != null)
            {
                this.DropDownItemClicked.Invoke(dropDownButton, pEventArgs);
            }
        }

        // the static instance of this singleton class
        private static InterfaceManager sInstance;

        // the dictionary of woz buttons
        private Dictionary<string, Woz.Button> mWozButtons;

        // the dictionary of comboboxes
        private Dictionary<string, ToolStripComboBox> mComboBoxes;

        // the dictionary of drop downs
        private Dictionary<string, ToolStripDropDownButton> mDropDownButtons;

        // the dictionary of menus
        private Dictionary<string, MenuStrip> mMenuStrips;

        // well use this to track our menus while initializing
        private MenuStrip mCurrentMenu;

        // the dictionary of toolstrips
        private Dictionary<string, ToolStrip> mToolStrips;

        // well use this to track our toolstrips while initializing
        private ToolStrip mCurrentToolStrip;

        // invoked when a drop down button has a new menu item clicked
        public event ToolStripItemClickedEventHandler DropDownItemClicked;
    }
}